/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package chatserver;

import java.net.Socket;
import shared.HeaderConstants;
import shared.MessageHandler;

/**
 *
 * @author freeman
 */
class ServerThread extends Thread {

    private Server server = null;
    private boolean isAlive = true;
    private int id = 0;
    private String userName = "#unregistered";
    private MessageHandler messageHandler = null;

    /*
     * Constructor, gets input and output streams for the client it 
     * will be talking to.
     * 
     * @param server: The server to forward messages to
     * @param clientSOcket: The socket on which the client is talking
     * @param id: The id given to this thread so the server can 
     *  identify it.
     */
    public ServerThread( Server server, Socket clientSocket, int id ) {
        /*
         * store instance vars so we know about the server to tell it about
         * things we receive, and so that we know our id.
         */
        this.server = server;
        this.id = id;

        /*establish the messagehandler*/
        messageHandler = MessageHandler.getInstance(new Integer(id).toString());

        /*make sure the msg we can set up the socket for reading and writing..*/
        if ( !messageHandler.setSocket( clientSocket ) ) {
            System.err.println( "ServerThread: Client (id " + this.id + ") Cannot init MessageHandler" );
            messageHandler = null;
        }
    }

    /*
     * Attempt to perform initialization of the client, and enter the 
     * message loop.
     */
    @Override
    public void run() {
        System.out.println( "ServerThread: Client (id " + this.id + ") is in run..." );
        if ( initClient() ) {
            /*this method will block until something terminates it*/
            receiveAndProcessLoop();
        } else {
            System.err.println( "ServerThread: Client (id " + this.id + ") did not init correctly." );
            this.terminate();
        }

        System.out.println( "ServerThread: Client (id " + this.id + ") exiting run..." );
    }

    /*
     * Init client follows the following protocol:
     * Receive header_init with the username:<username>
     * 
     * @return true if client inits properly.
     */
    public boolean initClient() {
        /*make sure we have a MessageHandler*/
        if ( messageHandler == null ) {
            System.err.println( "ServerThread: Client (id " + this.id + ") Cannot init Client (id "
                    + this.id + ") no MessageHandler configured." );
            return false;
        }

        /*read the clients initial handshake (currently just username)*/
        if ( !messageHandler.read() ) {
            System.err.println( "ServerThread: Client (id " + this.id + ") Error reading from "
                    + "client (id " + this.id + ")" );
            return false;
        }

        /*make sure the header was right*/
        if ( !messageHandler.headerEquals( HeaderConstants.HEADER_INIT ) ) {
            System.err.println( "ServerThread: Client (id " + this.id + ") Client send invalid header: "
                    + messageHandler.headerString() );
            return false;
        }

        /*parse the clients username*/
        String protoPrefix = "username:";
        try {
            userName = messageHandler.getMessageString().
                    substring( messageHandler.getMessageString().
                    indexOf( protoPrefix ) + protoPrefix.length() );
        } catch ( Exception e ) {
            System.err.println( "ServerThread: Client (id " + this.id + ") Could not parse username from "
                    + "client (id " + this.id + ") error: "
                    + e.getMessage() );
            return false;
        }

        /*send the response*/
        System.out.println( "ServerThread: Client (id " + this.id + ") Sending ACCEPT to "
                + "client (id " + this.id + ")..." );
        String msg = "Welcome to the server, " + userName + "!";
        if ( !this.sendData( HeaderConstants.HEADER_SVRACCEPT,
                msg.getBytes() ) ) {
            return false;
        }

        /*tell the server what our username is...*/
        server.serverRegisterUsername( userName, id );

        return true;
    }

    /*
     * Reads data from the client in a loop:
     * 1st read in the length of the message
     * 2nd read in the header byte
     * 3rd read in the message
     * 
     * Parsees the header to determine what to do.
     */
    public void receiveAndProcessLoop() {
        while ( isRunning() ) {
            System.out.println("ServerThread: Client (id " + this.id + ")"
                    + " blocking, waiting for data...");
            if ( !messageHandler.read() ) {
                System.err.println( "ServerThread: Client (id " + this.id +
                        ") receiveAndProcessLoop "
                        + "Cannot read message." );
                return;
            }
            
            System.out.println("ServerThread: Client (id " + this.id + ") "
                    + "sent header " + messageHandler.getHeader()[0] );

            /*get info from handler*/
            byte[] message = messageHandler.getMessageBytes();

            if ( messageHandler.headerEquals( HeaderConstants.HEADER_CHATMSG_PVT ) ) {
                /*to be impl*/
            }
            if ( messageHandler.headerEquals( HeaderConstants.HEADER_DISCONNECT ) ) {
                this.terminate();
            }
            if ( messageHandler.headerEquals( HeaderConstants.HEADER_CHATMSG_PUB ) ) {
                server.serverMessagePubChat( message, id );
            }

        }
        System.err.println("ServerThread: Client (id " + this.id +
                ") exiting receiveAndProcess...");
    }

    /*
     * Checks whether or not the serverthread is running.
     * 
     * @return true if the serverthread is running, false otherwise.
     */
    private synchronized boolean isRunning() {
        return isAlive;
    }

    /*
     * Terminates the serverthread.
     */
    private synchronized void terminate() {
        System.err.println( "ServerThread: Client (id "
                    + this.id + ") terminated." );
        server.serverRemoveMe( this.id );
        isAlive = false;
        this.interrupt();
    }

    /*
     * sends Data using the message handler.
     * 
     * @param header: The header to send to the handler.
     * @param message: The message to send to the handler.
     * 
     * @return false if the handler cannot write data, true otherwise
     */
    public boolean sendData( byte[] header, byte[] message ) {
        if ( messageHandler == null ) {
            System.err.println( "ServerThread Client (id " + this.id + ") cannot send data, messageHandler is null" );
            return false;
        }
        if ( !messageHandler.write( header, message ) ) {
            System.err.println( "ServerThread Client (id " + this.id + ") cannot write, messageHandler failed to write." );
            return false;
        }

        return true;
    }
}
